/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.primitives.cadmium;

import java.lang.reflect.Field;

import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.CodeRunner;
import fr.x9c.cadmium.kernel.Custom;
import fr.x9c.cadmium.kernel.Fail;
import fr.x9c.cadmium.kernel.Primitive;
import fr.x9c.cadmium.kernel.PrimitiveProvider;
import fr.x9c.cadmium.kernel.Value;

/**
 * This class provides primitives related to {@link java.lang.reflect.Field}.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
@PrimitiveProvider
public final class Fields {

    /**
     * No instance of this class.
     */
    private Fields() {
    } // end empty constructor

    /**
     * Executes {@link java.lang.reflect.Field#getClass()} method.
     * @param ctxt context
     * @param field object to execute method on
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_declaring_class(final CodeRunner ctxt,
                                                          final Value field)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            return Cadmium.createObject(f.getDeclaringClass());
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_declaring_class(CodeRunner, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getModifiers()} method.
     * @param ctxt context
     * @param field object to execute method on
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_modifiers(final CodeRunner ctxt,
                                                    final Value field)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            return Cadmium.createModifiersList(f.getModifiers());
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_modifiers(CodeRunner, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getName()} method.
     * @param ctxt context
     * @param field object to execute method on
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_name(final CodeRunner ctxt,
                                               final Value field)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            return Value.createFromBlock(Block.createString(f.getName()));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_name(CodeRunner, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#get(java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get(final CodeRunner ctxt,
                                          final Value field,
                                          final Value obj)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            final Object o = obj.asBlock().asCustom();
            return Cadmium.createObject(f.get(o));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getBoolean(java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_boolean(final CodeRunner ctxt,
                                                      final Value field,
                                                      final Value obj)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            final Object o = obj.asBlock().asCustom();
            return f.getBoolean(o) ? Value.TRUE : Value.FALSE;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_boolean(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getByte(java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_byte(final CodeRunner ctxt,
                                               final Value field,
                                               final Value obj)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            final Object o = obj.asBlock().asCustom();
            return Value.createFromLong(f.getByte(o));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_byte(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getChar(java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_char(final CodeRunner ctxt,
                                               final Value field,
                                               final Value obj)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            final Object o = obj.asBlock().asCustom();
            return Value.createFromLong(f.getChar(o));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_char(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getDouble(java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_double(final CodeRunner ctxt,
                                                 final Value field,
                                                 final Value obj)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            final Object o = obj.asBlock().asCustom();
            return Value.createFromBlock(Block.createDouble(f.getDouble(o)));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_double(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getFloat(java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_float(final CodeRunner ctxt,
                                                final Value field,
                                                final Value obj)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            final Object o = obj.asBlock().asCustom();
            return Value.createFromBlock(Block.createDouble(f.getFloat(o)));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_float(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getInt(java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_int(final CodeRunner ctxt,
                                              final Value field,
                                              final Value obj)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            final Object o = obj.asBlock().asCustom();
            final Block b = Block.createCustom(Custom.INT_32_SIZE,
                                               Custom.INT_32_OPS);
            b.setInt32(f.getInt(o));
            return Value.createFromBlock(b);
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_int(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getLong(java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_long(final CodeRunner ctxt,
                                               final Value field,
                                               final Value obj)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            final Object o = obj.asBlock().asCustom();
            final Block b = Block.createCustom(Custom.INT_64_SIZE,
                                               Custom.INT_64_OPS);
            b.setInt64(f.getLong(o));
            return Value.createFromBlock(b);
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_long(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#getShort(java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_get_short(final CodeRunner ctxt,
                                                final Value field,
                                                final Value obj)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            final Object o = obj.asBlock().asCustom();
            return Value.createFromLong(f.getShort(o));
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_get_short(CodeRunner, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#set(java.lang.Object, java.lang.Object)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_set(final CodeRunner ctxt,
                                          final Value field,
                                          final Value obj,
                                          final Value val)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            f.set(obj.asBlock().asCustom(), val.asBlock().asCustom());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_set(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#setBoolean(java.lang.Object, boolean)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_set_boolean(final CodeRunner ctxt,
                                                  final Value field,
                                                  final Value obj,
                                                  final Value val)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            f.setBoolean(obj.asBlock().asCustom(), val == Value.TRUE);
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_set_boolean(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#setByte(java.lang.Object, byte)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_set_byte(final CodeRunner ctxt,
                                               final Value field,
                                               final Value obj,
                                               final Value val)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            f.setByte(obj.asBlock().asCustom(), (byte) val.asLong());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_set_byte(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#setChar(java.lang.Object, char)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_set_char(final CodeRunner ctxt,
                                               final Value field,
                                               final Value obj,
                                               final Value val)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            f.setChar(obj.asBlock().asCustom(), (char) val.asLong());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_set_char(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#setDouble(java.lang.Object, double)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_set_double(final CodeRunner ctxt,
                                                 final Value field,
                                                 final Value obj,
                                                 final Value val)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            f.setDouble(obj.asBlock().asCustom(), val.asBlock().asDouble());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_set_double(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#setFloat(java.lang.Object, float)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_set_float(final CodeRunner ctxt,
                                                final Value field,
                                                final Value obj,
                                                final Value val)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            f.setFloat(obj.asBlock().asCustom(), (float) val.asBlock().asDouble());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_set_float(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#setInt(java.lang.Object, int)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_set_int(final CodeRunner ctxt,
                                              final Value field,
                                              final Value obj,
                                              final Value val)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            f.setInt(obj.asBlock().asCustom(), val.asBlock().asInt32());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_set_int(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#setLong(java.lang.Object, long)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_set_long(final CodeRunner ctxt,
                                               final Value field,
                                               final Value obj,
                                               final Value val)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            f.setLong(obj.asBlock().asCustom(), val.asBlock().asInt64());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_set_long(CodeRunner, Value, Value, Value)'

    /**
     * Executes {@link java.lang.reflect.Field#setShort(java.lang.Object, short)} method.
     * @param ctxt context
     * @param field object to execute method on
     * @param obj parameter for method execution
     * @param val parameter for method execution
     * @return result of method execution
     * @throws Fail.Exception if Java method throws an exception
     */
    @Primitive
    public static Value cadmium_field_set_short(final CodeRunner ctxt,
                                                final Value field,
                                                final Value obj,
                                                final Value val)
        throws Fail.Exception {
        try {
            final Field f = (Field) field.asBlock().asCustom();
            f.setShort(obj.asBlock().asCustom(), (short) val.asLong());
            return Value.UNIT;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_set_short(CodeRunner, Value, Value, Value)'

    /**
     * Casts an object to a field.
     * @param ctxt context
     * @param obj object to cast
     * @return object as field
     * @throws Fail.Exception if cast fails
     */
    @Primitive
    public static Value cadmium_field_as_field(final CodeRunner ctxt,
                                               final Value obj)
        throws Fail.Exception {
        try {
            final Block objBlock = obj.asBlock();
            objBlock.setCustom((Field) objBlock.asCustom());
            return obj;
        } catch (final Exception e) {
            Cadmium.fail(ctxt, e);
            return Value.UNIT; // never reached
        } // end try/catch
    } // end method 'cadmium_field_as_field(CodeRunner, Value)'

} // end class 'Fields'
